#include "eventDriven.h"

#include <stdlib.h>

int initEventEmitter(EventEmitter *events, EventHandler* handlers, int eventSize, int handleSize) 
{
    if(events == NULL || handlers == NULL || eventSize <= 0)
        return EVENT_HDL_ERR_PARAM;
    events->handlers = handlers;
    events->queue = malloc(sizeof(EventQueue));
    events->handleSize = handleSize;
    if(events->queue == NULL)
        return EVENT_HDL_ERR_MEM;
    events->queue->events = malloc(sizeof(Event) * eventSize);
    if(events->queue->events == NULL)
        return EVENT_HDL_ERR_MEM;
    events->queue->head = 0;
    events->queue->tail = 0;
    events->queue->size = eventSize;
    events->queue->count = 0;
    if(sem_init(&events->queue->sem, 0, 0) != 0)
        return EVENT_HDL_ERR_MEM;
    if(pthread_mutex_init(&events->queue->mutex, NULL)!= 0)
        return EVENT_HDL_ERR_MEM;
    return EVENT_HDL_SUCCESS;
}

void distoryEventEmitter(EventEmitter *events) {
    if(events == NULL)
        return;
    if(events->queue != NULL) {
        if(events->queue->events != NULL)
            free(events->queue->events);
        free(events->queue);
        sem_destroy(&events->queue->sem);
        pthread_mutex_destroy(&events->queue->mutex);
    }
    events->handlers = NULL;
    events->queue = NULL;
}

int pushEvent(EventEmitter *events, const Event event) {
    if(events == NULL || events->queue == NULL || events->queue->events == NULL)
        return EVENT_HDL_ERR_PARAM;
    pthread_mutex_lock(&events->queue->mutex);
    if(events->queue->count == events->queue->size){
        pthread_mutex_unlock(&events->queue->mutex);
        return EVENT_HDL_ERR_FULL;
    }
    events->queue->events[events->queue->head] = event;
    events->queue->head = (events->queue->head + 1) % events->queue->size;
    events->queue->count++;
    sem_post(&events->queue->sem);
    pthread_mutex_unlock(&events->queue->mutex);
    return EVENT_HDL_SUCCESS;
}

int popEvent(EventEmitter *events, Event* event) {
    if(events == NULL || events->queue == NULL || events->queue->events == NULL)
        return EVENT_HDL_ERR_PARAM;
    sem_wait(&events->queue->sem);
    pthread_mutex_lock(&events->queue->mutex);
    if(events->queue->count == 0){
        pthread_mutex_unlock(&events->queue->mutex);
        return EVENT_HDL_ERR_EMPTY;
    }
    *event = events->queue->events[events->queue->tail];
    events->queue->tail = (events->queue->tail + 1) % events->queue->size;
    events->queue->count--;
    pthread_mutex_unlock(&events->queue->mutex);
    return EVENT_HDL_SUCCESS;
}

int handleEventOnce(EventEmitter *events){
    if(events == NULL || events->queue == NULL || events->queue->events == NULL)
        return EVENT_HDL_ERR_PARAM;
    Event event;
    int ret = popEvent(events, &event);
    if( ret != EVENT_HDL_SUCCESS)
        return ret;
    int eventSize = events->handleSize;
    for (int i = 0; i < eventSize; i++) {
        if (events->handlers[i].type == event.type) {
            if(events->handlers[i].callback!= NULL)
                events->handlers[i].callback(&event);
            return EVENT_HDL_SUCCESS;
        }
    }
}

void *handleEvent(void *event){
    EventEmitter *events = (EventEmitter*)event;
    if(events == NULL || events->queue == NULL || events->queue->events == NULL)
        return NULL;
    while(1){
        handleEventOnce(events);
    }
}